/*
* Cmd.java
* Copyright (C) 2003
* 
* $Id: Cmd.java,v 1.18 2006/01/21 21:53:32 salomo Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using M_Player = Suake2.UI.game.monsters.M_Player;
using Suake2.UI.qcommon;
using SV_GAME = Suake2.UI.server.SV_GAME;
using Lib = Suake2.UI.util.Lib;
namespace Suake2.UI.game
{
	
	/// <summary> Cmd</summary>
	public sealed class Cmd
	{
		public Cmd()
		{
			InitBlock();
		}
		public class AnonymousClassxcommand_t:xcommand_t
		{
			public override void  execute()
			{
				cmd_function_t cmd = Cmd.cmd_functions;
				int i = 0;
				
				while (cmd != null)
				{
					Com.Printf(cmd.name + '\n');
					i++;
					cmd = cmd.next;
				}
				Com.Printf(i + " commands\n");
			}
		}
		public class AnonymousClassxcommand_t1:xcommand_t
		{
			public override void  execute()
			{
				if (Cmd.Argc() != 2)
				{
					Com.Printf("exec <filename> : execute a script file\n");
					return ;
				}
				
				sbyte[] f = null;
				f = FS.LoadFile(Cmd.Argv(1));
				if (f == null)
				{
					Com.Printf("couldn't exec " + Cmd.Argv(1) + "\n");
					return ;
				}
				Com.Printf("execing " + Cmd.Argv(1) + "\n");
				
				Cbuf.InsertText(new System.String(SupportClass.ToCharArray(SupportClass.ToByteArray(f))));
				
				FS.FreeFile(f);
			}
		}
		public class AnonymousClassxcommand_t2:xcommand_t
		{
			public override void  execute()
			{
				for (int i = 1; i < Cmd.Argc(); i++)
				{
					Com.Printf(Cmd.Argv(i) + " ");
				}
				Com.Printf("'\n");
			}
		}
		public class AnonymousClassxcommand_t3:xcommand_t
		{
			public override void  execute()
			{
				cmdalias_t a = null;
				if (Cmd.Argc() == 1)
				{
					Com.Printf("Current alias commands:\n");
					for (a = Globals.cmd_alias; a != null; a = a.next)
					{
						Com.Printf(a.name + " : " + a.value_Renamed);
					}
					return ;
				}
				
				System.String s = Cmd.Argv(1);
				if (s.Length > Defines.MAX_ALIAS_NAME)
				{
					Com.Printf("Alias name is too long\n");
					return ;
				}
				
				// if the alias already exists, reuse it
				for (a = Globals.cmd_alias; a != null; a = a.next)
				{
					if (s.ToUpper().Equals(a.name.ToUpper()))
					{
						a.value_Renamed = null;
						break;
					}
				}
				
				if (a == null)
				{
					a = new cmdalias_t();
					a.next = Globals.cmd_alias;
					Globals.cmd_alias = a;
				}
				a.name = s;
				
				// copy the rest of the command line
				System.String cmd = "";
				int c = Cmd.Argc();
				for (int i = 2; i < c; i++)
				{
					cmd = cmd + Cmd.Argv(i);
					if (i != (c - 1))
						cmd = cmd + " ";
				}
				cmd = cmd + "\n";
				
				a.value_Renamed = cmd;
			}
		}
		public class AnonymousClassxcommand_t4:xcommand_t
		{
			public override void  execute()
			{
				Globals.cmd_wait = true;
			}
		}
		internal class AnonymousClassComparator : System.Collections.IComparer
		{
			public int Compare(System.Object o1, System.Object o2)
			{
				int anum = ((System.Int32) o1);
				int bnum = ((System.Int32) o2);
				
				int anum1 = GameBase.game.clients[anum].ps.stats[Defines.STAT_FRAGS];
				int bnum1 = GameBase.game.clients[bnum].ps.stats[Defines.STAT_FRAGS];
				
				if (anum1 < bnum1)
					return - 1;
				if (anum1 > bnum1)
					return 1;
				return 0;
			}
		}
		private void  InitBlock()
		{
			cmd_argv = ;
			expanded = ;
			temporary = ;
		}
		//UPGRADE_NOTE: The initialization of  'List_f' was moved to static method 'jake2.game.Cmd'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t List_f;
		
		//UPGRADE_NOTE: The initialization of  'Exec_f' was moved to static method 'jake2.game.Cmd'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t Exec_f;
		
		//UPGRADE_NOTE: The initialization of  'Echo_f' was moved to static method 'jake2.game.Cmd'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t Echo_f;
		
		//UPGRADE_NOTE: The initialization of  'Alias_f' was moved to static method 'jake2.game.Cmd'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t Alias_f;
		
		//UPGRADE_NOTE: The initialization of  'Wait_f' was moved to static method 'jake2.game.Cmd'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static xcommand_t Wait_f;
		
		public static cmd_function_t cmd_functions = null;
		
		public static int cmd_argc;
		
		//UPGRADE_NOTE: The initialization of  'cmd_argv' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static System.String[] cmd_argv;
		
		public static System.String cmd_args;
		
		public const int ALIAS_LOOP_COUNT = 16;
		
		/// <summary> Register our commands.</summary>
		public static void  Init()
		{
			
			Cmd.AddCommand("exec", Exec_f);
			Cmd.AddCommand("echo", Echo_f);
			Cmd.AddCommand("cmdlist", List_f);
			Cmd.AddCommand("alias", Alias_f);
			Cmd.AddCommand("wait", Wait_f);
		}
		
		//UPGRADE_NOTE: The initialization of  'expanded' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		private static char[] expanded;
		
		//UPGRADE_NOTE: The initialization of  'temporary' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		private static char[] temporary;
		
		//UPGRADE_NOTE: The initialization of  'PlayerSort' was moved to static method 'jake2.game.Cmd'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static System.Collections.IComparer PlayerSort;
		
		/// <summary> Cmd_MacroExpandString.</summary>
		public static char[] MacroExpandString(char[] text, int len)
		{
			int i, j, count;
			bool inquote;
			
			char[] scan;
			
			System.String token;
			inquote = false;
			
			scan = text;
			
			if (len >= Defines.MAX_STRING_CHARS)
			{
				Com.Printf("Line exceeded " + Defines.MAX_STRING_CHARS + " chars, discarded.\n");
				return null;
			}
			
			count = 0;
			
			for (i = 0; i < len; i++)
			{
				if (scan[i] == '"')
					inquote = !inquote;
				
				if (inquote)
					continue; // don't expand inside quotes
				
				if (scan[i] != '$')
					continue;
				
				// scan out the complete macro, without $
				Com.ParseHelp ph = new Com.ParseHelp(text, i + 1);
				token = Com.Parse(ph);
				
				if (ph.data == null)
					continue;
				
				token = Cvar.VariableString(token);
				
				j = token.Length;
				
				len += j;
				
				if (len >= Defines.MAX_STRING_CHARS)
				{
					Com.Printf("Expanded line exceeded " + Defines.MAX_STRING_CHARS + " chars, discarded.\n");
					return null;
				}
				
				Array.Copy(scan, 0, temporary, 0, i);
				Array.Copy(token.ToCharArray(), 0, temporary, i, token.Length);
				Array.Copy(ph.data, ph.index, temporary, i + j, len - ph.index - j);
				
				Array.Copy(temporary, 0, expanded, 0, 0);
				scan = expanded;
				i--;
				if (++count == 100)
				{
					Com.Printf("Macro expansion loop, discarded.\n");
					return null;
				}
			}
			
			if (inquote)
			{
				Com.Printf("Line has unmatched quote, discarded.\n");
				return null;
			}
			
			return scan;
		}
		
		/// <summary> Cmd_TokenizeString
		/// 
		/// Parses the given string into command line tokens. $Cvars will be expanded
		/// unless they are in a quoted token.
		/// </summary>
		public static void  TokenizeString(char[] text, bool macroExpand)
		{
			System.String com_token;
			
			cmd_argc = 0;
			cmd_args = "";
			
			int len = Lib.strlen(text);
			
			// macro expand the text
			if (macroExpand)
				text = MacroExpandString(text, len);
			
			if (text == null)
				return ;
			
			len = Lib.strlen(text);
			
			Com.ParseHelp ph = new Com.ParseHelp(text);
			
			while (true)
			{
				
				// skip whitespace up to a /n
				char c = ph.skipwhitestoeol();
				
				if (c == '\n')
				{
					// a newline seperates commands in the buffer
					c = ph.nextchar();
					break;
				}
				
				if (c == 0)
					return ;
				
				// set cmd_args to everything after the first arg
				if (cmd_argc == 1)
				{
					cmd_args = new System.String(text, ph.index, len - ph.index);
					cmd_args.Trim();
				}
				
				com_token = Com.Parse(ph);
				
				if (ph.data == null)
					return ;
				
				if (cmd_argc < Defines.MAX_STRING_TOKENS)
				{
					cmd_argv[cmd_argc] = com_token;
					cmd_argc++;
				}
			}
		}
		
		public static void  AddCommand(System.String cmd_name, xcommand_t function)
		{
			cmd_function_t cmd;
			//Com.DPrintf("Cmd_AddCommand: " + cmd_name + "\n");
			// fail if the command is a variable name
			if ((Cvar.VariableString(cmd_name)).Length > 0)
			{
				Com.Printf("Cmd_AddCommand: " + cmd_name + " already defined as a var\n");
				return ;
			}
			
			// fail if the command already exists
			for (cmd = cmd_functions; cmd != null; cmd = cmd.next)
			{
				if (cmd_name.Equals(cmd.name))
				{
					Com.Printf("Cmd_AddCommand: " + cmd_name + " already defined\n");
					return ;
				}
			}
			
			cmd = new cmd_function_t();
			cmd.name = cmd_name;
			
			cmd.function = function;
			cmd.next = cmd_functions;
			cmd_functions = cmd;
		}
		
		/// <summary> Cmd_RemoveCommand </summary>
		public static void  RemoveCommand(System.String cmd_name)
		{
			cmd_function_t cmd, back = null;
			
			back = cmd = cmd_functions;
			
			while (true)
			{
				
				if (cmd == null)
				{
					Com.Printf("Cmd_RemoveCommand: " + cmd_name + " not added\n");
					return ;
				}
				if (0 == Lib.strcmp(cmd_name, cmd.name))
				{
					if (cmd == cmd_functions)
						cmd_functions = cmd.next;
					else
						back.next = cmd.next;
					return ;
				}
				back = cmd;
				cmd = cmd.next;
			}
		}
		
		/// <summary> Cmd_Exists </summary>
		public static bool Exists(System.String cmd_name)
		{
			cmd_function_t cmd;
			
			for (cmd = cmd_functions; cmd != null; cmd = cmd.next)
			{
				if (cmd.name.Equals(cmd_name))
					return true;
			}
			
			return false;
		}
		
		public static int Argc()
		{
			return cmd_argc;
		}
		
		public static System.String Argv(int i)
		{
			if (i < 0 || i >= cmd_argc)
				return "";
			return cmd_argv[i];
		}
		
		public static System.String Args()
		{
			return new System.Text.StringBuilder(cmd_args).ToString();
		}
		
		/// <summary> Cmd_ExecuteString
		/// 
		/// A complete command line has been parsed, so try to execute it 
		/// FIXME: lookupnoadd the token to speed search? 
		/// </summary>
		public static void  ExecuteString(System.String text)
		{
			
			cmd_function_t cmd;
			cmdalias_t a;
			
			TokenizeString(text.ToCharArray(), true);
			
			// execute the command line
			if (Argc() == 0)
				return ; // no tokens
			
			// check functions
			for (cmd = cmd_functions; cmd != null; cmd = cmd.next)
			{
				if (cmd_argv[0].ToUpper().Equals(cmd.name.ToUpper()))
				{
					if (null == cmd.function)
					{
						// forward to server command
						Cmd.ExecuteString("cmd " + text);
					}
					else
					{
						cmd.function.execute();
					}
					return ;
				}
			}
			
			// check alias
			for (a = Globals.cmd_alias; a != null; a = a.next)
			{
				
				if (cmd_argv[0].ToUpper().Equals(a.name.ToUpper()))
				{
					
					if (++Globals.alias_count == ALIAS_LOOP_COUNT)
					{
						Com.Printf("ALIAS_LOOP_COUNT\n");
						return ;
					}
					Cbuf.InsertText(a.value_Renamed);
					return ;
				}
			}
			
			// check cvars
			if (Cvar.Command())
				return ;
			
			// send it as a server command if we are connected
			Cmd.ForwardToServer();
		}
		
		/// <summary> Cmd_Give_f
		/// 
		/// Give items to a client.
		/// </summary>
		public static void  Give_f(edict_t ent)
		{
			System.String name;
			gitem_t it;
			int index;
			int i;
			bool give_all;
			edict_t it_ent;
			
			if (GameBase.deathmatch.value_Renamed != 0 && GameBase.sv_cheats.value_Renamed == 0)
			{
				SV_GAME.PF_cprintfhigh(ent, "You must run the server with '+set cheats 1' to enable this command.\n");
				return ;
			}
			
			name = Cmd.Args();
			
			if (0 == Lib.Q_stricmp(name, "all"))
				give_all = true;
			else
				give_all = false;
			
			if (give_all || 0 == Lib.Q_stricmp(Cmd.Argv(1), "health"))
			{
				if (Cmd.Argc() == 3)
					ent.health = Lib.atoi(Cmd.Argv(2));
				else
					ent.health = ent.max_health;
				if (!give_all)
					return ;
			}
			
			if (give_all || 0 == Lib.Q_stricmp(name, "weapons"))
			{
				for (i = 1; i < GameBase.game.num_items; i++)
				{
					it = GameItemList.itemlist[i];
					if (null == it.pickup)
						continue;
					if (0 == (it.flags & Defines.IT_WEAPON))
						continue;
					ent.client.pers.inventory[i] += 1;
				}
				if (!give_all)
					return ;
			}
			
			if (give_all || 0 == Lib.Q_stricmp(name, "ammo"))
			{
				for (i = 1; i < GameBase.game.num_items; i++)
				{
					it = GameItemList.itemlist[i];
					if (null == it.pickup)
						continue;
					if (0 == (it.flags & Defines.IT_AMMO))
						continue;
					GameItems.Add_Ammo(ent, it, 1000);
				}
				if (!give_all)
					return ;
			}
			
			if (give_all || Lib.Q_stricmp(name, "armor") == 0)
			{
				gitem_armor_t info;
				
				it = GameItems.FindItem("Jacket Armor");
				ent.client.pers.inventory[GameItems.ITEM_INDEX(it)] = 0;
				
				it = GameItems.FindItem("Combat Armor");
				ent.client.pers.inventory[GameItems.ITEM_INDEX(it)] = 0;
				
				it = GameItems.FindItem("Body Armor");
				info = (gitem_armor_t) it.info;
				ent.client.pers.inventory[GameItems.ITEM_INDEX(it)] = info.max_count;
				
				if (!give_all)
					return ;
			}
			
			if (give_all || Lib.Q_stricmp(name, "Power Shield") == 0)
			{
				it = GameItems.FindItem("Power Shield");
				it_ent = GameUtil.G_Spawn();
				it_ent.classname = it.classname;
				GameItems.SpawnItem(it_ent, it);
				GameItems.Touch_Item(it_ent, ent, GameBase.dummyplane, null);
				if (it_ent.inuse)
					GameUtil.G_FreeEdict(it_ent);
				
				if (!give_all)
					return ;
			}
			
			if (give_all)
			{
				for (i = 1; i < GameBase.game.num_items; i++)
				{
					it = GameItemList.itemlist[i];
					if (it.pickup != null)
						continue;
					if ((it.flags & (Defines.IT_ARMOR | Defines.IT_WEAPON | Defines.IT_AMMO)) != 0)
						continue;
					ent.client.pers.inventory[i] = 1;
				}
				return ;
			}
			
			it = GameItems.FindItem(name);
			if (it == null)
			{
				name = Cmd.Argv(1);
				it = GameItems.FindItem(name);
				if (it == null)
				{
					SV_GAME.PF_cprintf(ent, Defines.PRINT_HIGH, "unknown item\n");
					return ;
				}
			}
			
			if (it.pickup == null)
			{
				SV_GAME.PF_cprintf(ent, Defines.PRINT_HIGH, "non-pickup item\n");
				return ;
			}
			
			index = GameItems.ITEM_INDEX(it);
			
			if ((it.flags & Defines.IT_AMMO) != 0)
			{
				if (Cmd.Argc() == 3)
					ent.client.pers.inventory[index] = Lib.atoi(Cmd.Argv(2));
				else
					ent.client.pers.inventory[index] += it.quantity;
			}
			else
			{
				it_ent = GameUtil.G_Spawn();
				it_ent.classname = it.classname;
				GameItems.SpawnItem(it_ent, it);
				GameItems.Touch_Item(it_ent, ent, GameBase.dummyplane, null);
				if (it_ent.inuse)
					GameUtil.G_FreeEdict(it_ent);
			}
		}
		
		/// <summary> Cmd_God_f
		/// 
		/// Sets client to godmode
		/// 
		/// argv(0) god
		/// </summary>
		public static void  God_f(edict_t ent)
		{
			System.String msg;
			
			if (GameBase.deathmatch.value_Renamed != 0 && GameBase.sv_cheats.value_Renamed == 0)
			{
				SV_GAME.PF_cprintfhigh(ent, "You must run the server with '+set cheats 1' to enable this command.\n");
				return ;
			}
			
			ent.flags ^= Defines.FL_GODMODE;
			if (0 == (ent.flags & Defines.FL_GODMODE))
				msg = "godmode OFF\n";
			else
				msg = "godmode ON\n";
			
			SV_GAME.PF_cprintf(ent, Defines.PRINT_HIGH, msg);
		}
		
		/// <summary> Cmd_Notarget_f
		/// 
		/// Sets client to notarget
		/// 
		/// argv(0) notarget.
		/// </summary>
		public static void  Notarget_f(edict_t ent)
		{
			System.String msg;
			
			if (GameBase.deathmatch.value_Renamed != 0 && GameBase.sv_cheats.value_Renamed == 0)
			{
				SV_GAME.PF_cprintfhigh(ent, "You must run the server with '+set cheats 1' to enable this command.\n");
				return ;
			}
			
			ent.flags ^= Defines.FL_NOTARGET;
			if (0 == (ent.flags & Defines.FL_NOTARGET))
				msg = "notarget OFF\n";
			else
				msg = "notarget ON\n";
			
			SV_GAME.PF_cprintfhigh(ent, msg);
		}
		
		/// <summary> Cmd_Noclip_f
		/// 
		/// argv(0) noclip.
		/// </summary>
		public static void  Noclip_f(edict_t ent)
		{
			System.String msg;
			
			if (GameBase.deathmatch.value_Renamed != 0 && GameBase.sv_cheats.value_Renamed == 0)
			{
				SV_GAME.PF_cprintfhigh(ent, "You must run the server with '+set cheats 1' to enable this command.\n");
				return ;
			}
			
			if (ent.movetype == Defines.MOVETYPE_NOCLIP)
			{
				ent.movetype = Defines.MOVETYPE_WALK;
				msg = "noclip OFF\n";
			}
			else
			{
				ent.movetype = Defines.MOVETYPE_NOCLIP;
				msg = "noclip ON\n";
			}
			
			SV_GAME.PF_cprintfhigh(ent, msg);
		}
		
		/// <summary> Cmd_Use_f
		/// 
		/// Use an inventory item.
		/// </summary>
		public static void  Use_f(edict_t ent)
		{
			int index;
			gitem_t it;
			System.String s;
			
			s = Cmd.Args();
			
			it = GameItems.FindItem(s);
			Com.dprintln("using:" + s);
			if (it == null)
			{
				SV_GAME.PF_cprintfhigh(ent, "unknown item: " + s + "\n");
				return ;
			}
			if (it.use == null)
			{
				SV_GAME.PF_cprintfhigh(ent, "Item is not usable.\n");
				return ;
			}
			index = GameItems.ITEM_INDEX(it);
			if (0 == ent.client.pers.inventory[index])
			{
				SV_GAME.PF_cprintfhigh(ent, "Out of item: " + s + "\n");
				return ;
			}
			
			it.use.use(ent, it);
		}
		
		/// <summary> Cmd_Drop_f
		/// 
		/// Drop an inventory item.
		/// </summary>
		public static void  Drop_f(edict_t ent)
		{
			int index;
			gitem_t it;
			System.String s;
			
			s = Cmd.Args();
			it = GameItems.FindItem(s);
			if (it == null)
			{
				SV_GAME.PF_cprintfhigh(ent, "unknown item: " + s + "\n");
				return ;
			}
			if (it.drop == null)
			{
				SV_GAME.PF_cprintf(ent, Defines.PRINT_HIGH, "Item is not dropable.\n");
				return ;
			}
			index = GameItems.ITEM_INDEX(it);
			if (0 == ent.client.pers.inventory[index])
			{
				SV_GAME.PF_cprintfhigh(ent, "Out of item: " + s + "\n");
				return ;
			}
			
			it.drop.drop(ent, it);
		}
		
		/// <summary> Cmd_Inven_f.</summary>
		public static void  Inven_f(edict_t ent)
		{
			int i;
			gclient_t cl;
			
			cl = ent.client;
			
			cl.showscores = false;
			cl.showhelp = false;
			
			if (cl.showinventory)
			{
				cl.showinventory = false;
				return ;
			}
			
			cl.showinventory = true;
			
			GameBase.gi.WriteByte(Defines.svc_inventory);
			for (i = 0; i < Defines.MAX_ITEMS; i++)
			{
				GameBase.gi.WriteShort(cl.pers.inventory[i]);
			}
			GameBase.gi.unicast(ent, true);
		}
		
		/// <summary> Cmd_InvUse_f.</summary>
		public static void  InvUse_f(edict_t ent)
		{
			gitem_t it;
			
			Cmd.ValidateSelectedItem(ent);
			
			if (ent.client.pers.selected_item == - 1)
			{
				SV_GAME.PF_cprintfhigh(ent, "No item to use.\n");
				return ;
			}
			
			it = GameItemList.itemlist[ent.client.pers.selected_item];
			if (it.use == null)
			{
				SV_GAME.PF_cprintfhigh(ent, "Item is not usable.\n");
				return ;
			}
			it.use.use(ent, it);
		}
		
		/// <summary> Cmd_WeapPrev_f.</summary>
		public static void  WeapPrev_f(edict_t ent)
		{
			gclient_t cl;
			int i, index;
			gitem_t it;
			int selected_weapon;
			
			cl = ent.client;
			
			if (cl.pers.weapon == null)
				return ;
			
			selected_weapon = GameItems.ITEM_INDEX(cl.pers.weapon);
			
			// scan for the next valid one
			for (i = 1; i <= Defines.MAX_ITEMS; i++)
			{
				index = (selected_weapon + i) % Defines.MAX_ITEMS;
				if (0 == cl.pers.inventory[index])
					continue;
				
				it = GameItemList.itemlist[index];
				if (it.use == null)
					continue;
				
				if (0 == (it.flags & Defines.IT_WEAPON))
					continue;
				it.use.use(ent, it);
				if (cl.pers.weapon == it)
					return ; // successful
			}
		}
		
		/// <summary> Cmd_WeapNext_f.</summary>
		public static void  WeapNext_f(edict_t ent)
		{
			gclient_t cl;
			int i, index;
			gitem_t it;
			int selected_weapon;
			
			cl = ent.client;
			
			if (null == cl.pers.weapon)
				return ;
			
			selected_weapon = GameItems.ITEM_INDEX(cl.pers.weapon);
			
			// scan for the next valid one
			for (i = 1; i <= Defines.MAX_ITEMS; i++)
			{
				index = (selected_weapon + Defines.MAX_ITEMS - i) % Defines.MAX_ITEMS;
				//bugfix rst
				if (index == 0)
					index++;
				if (0 == cl.pers.inventory[index])
					continue;
				it = GameItemList.itemlist[index];
				if (null == it.use)
					continue;
				if (0 == (it.flags & Defines.IT_WEAPON))
					continue;
				it.use.use(ent, it);
				if (cl.pers.weapon == it)
					return ; // successful
			}
		}
		
		/// <summary> Cmd_WeapLast_f.</summary>
		public static void  WeapLast_f(edict_t ent)
		{
			gclient_t cl;
			int index;
			gitem_t it;
			
			cl = ent.client;
			
			if (null == cl.pers.weapon || null == cl.pers.lastweapon)
				return ;
			
			index = GameItems.ITEM_INDEX(cl.pers.lastweapon);
			if (0 == cl.pers.inventory[index])
				return ;
			it = GameItemList.itemlist[index];
			if (null == it.use)
				return ;
			if (0 == (it.flags & Defines.IT_WEAPON))
				return ;
			it.use.use(ent, it);
		}
		
		/// <summary> Cmd_InvDrop_f </summary>
		public static void  InvDrop_f(edict_t ent)
		{
			gitem_t it;
			
			Cmd.ValidateSelectedItem(ent);
			
			if (ent.client.pers.selected_item == - 1)
			{
				SV_GAME.PF_cprintfhigh(ent, "No item to drop.\n");
				return ;
			}
			
			it = GameItemList.itemlist[ent.client.pers.selected_item];
			if (it.drop == null)
			{
				SV_GAME.PF_cprintfhigh(ent, "Item is not dropable.\n");
				return ;
			}
			it.drop.drop(ent, it);
		}
		
		/// <summary> Cmd_Score_f
		/// 
		/// Display the scoreboard.
		/// 
		/// </summary>
		public static void  Score_f(edict_t ent)
		{
			ent.client.showinventory = false;
			ent.client.showhelp = false;
			
			if (0 == GameBase.deathmatch.value_Renamed && 0 == GameBase.coop.value_Renamed)
				return ;
			
			if (ent.client.showscores)
			{
				ent.client.showscores = false;
				return ;
			}
			
			ent.client.showscores = true;
			PlayerHud.DeathmatchScoreboard(ent);
		}
		
		/// <summary> Cmd_Help_f
		/// 
		/// Display the current help message. 
		/// 
		/// </summary>
		public static void  Help_f(edict_t ent)
		{
			// this is for backwards compatability
			if (GameBase.deathmatch.value_Renamed != 0)
			{
				Score_f(ent);
				return ;
			}
			
			ent.client.showinventory = false;
			ent.client.showscores = false;
			
			if (ent.client.showhelp && (ent.client.pers.game_helpchanged == GameBase.game.helpchanged))
			{
				ent.client.showhelp = false;
				return ;
			}
			
			ent.client.showhelp = true;
			ent.client.pers.helpchanged = 0;
			PlayerHud.HelpComputer(ent);
		}
		
		/// <summary> Cmd_Kill_f</summary>
		public static void  Kill_f(edict_t ent)
		{
			if ((GameBase.level.time - ent.client.respawn_time) < 5)
				return ;
			ent.flags &= ~ Defines.FL_GODMODE;
			ent.health = 0;
			GameBase.meansOfDeath = Defines.MOD_SUICIDE;
			PlayerClient.player_die.die(ent, ent, ent, 100000, Globals.vec3_origin);
		}
		
		/// <summary> Cmd_PutAway_f</summary>
		public static void  PutAway_f(edict_t ent)
		{
			ent.client.showscores = false;
			ent.client.showhelp = false;
			ent.client.showinventory = false;
		}
		
		/// <summary> Cmd_Players_f</summary>
		public static void  Players_f(edict_t ent)
		{
			int i;
			int count;
			System.String small;
			System.String large;
			
			System.Int32[] index = new System.Int32[256];
			
			count = 0;
			for (i = 0; i < GameBase.maxclients.value_Renamed; i++)
			{
				if (GameBase.game.clients[i].pers.connected)
				{
					index[count] = (System.Int32) i;
					count++;
				}
			}
			
			// sort by frags
			System.Array.Sort(index, 0, count - 1 - 0, Cmd.PlayerSort);
			
			// print information
			large = "";
			
			for (i = 0; i < count; i++)
			{
				small = GameBase.game.clients[index[i]].ps.stats[Defines.STAT_FRAGS] + " " + GameBase.game.clients[index[i]].pers.netname + "\n";
				
				if (small.Length + large.Length > 1024 - 100)
				{
					// can't print all of them in one packet
					large += "...\n";
					break;
				}
				large += small;
			}
			
			SV_GAME.PF_cprintfhigh(ent, large + "\n" + count + " players\n");
		}
		
		/// <summary> Cmd_Wave_f</summary>
		public static void  Wave_f(edict_t ent)
		{
			int i;
			
			i = Lib.atoi(Cmd.Argv(1));
			
			// can't wave when ducked
			if ((ent.client.ps.pmove.pm_flags & pmove_t.PMF_DUCKED) != 0)
				return ;
			
			if (ent.client.anim_priority > Defines.ANIM_WAVE)
				return ;
			
			ent.client.anim_priority = Defines.ANIM_WAVE;
			
			switch (i)
			{
				
				case 0: 
					SV_GAME.PF_cprintfhigh(ent, "flipoff\n");
					ent.s.frame = M_Player.FRAME_flip01 - 1;
					ent.client.anim_end = M_Player.FRAME_flip12;
					break;
				
				case 1: 
					SV_GAME.PF_cprintfhigh(ent, "salute\n");
					ent.s.frame = M_Player.FRAME_salute01 - 1;
					ent.client.anim_end = M_Player.FRAME_salute11;
					break;
				
				case 2: 
					SV_GAME.PF_cprintfhigh(ent, "taunt\n");
					ent.s.frame = M_Player.FRAME_taunt01 - 1;
					ent.client.anim_end = M_Player.FRAME_taunt17;
					break;
				
				case 3: 
					SV_GAME.PF_cprintfhigh(ent, "wave\n");
					ent.s.frame = M_Player.FRAME_wave01 - 1;
					ent.client.anim_end = M_Player.FRAME_wave11;
					break;
				
				case 4: 
				default: 
					SV_GAME.PF_cprintfhigh(ent, "point\n");
					ent.s.frame = M_Player.FRAME_point01 - 1;
					ent.client.anim_end = M_Player.FRAME_point12;
					break;
				}
		}
		
		/// <summary> Command to print the players own position.</summary>
		public static void  ShowPosition_f(edict_t ent)
		{
			SV_GAME.PF_cprintfhigh(ent, "pos=" + Lib.vtofsbeaty(ent.s.origin) + "\n");
		}
		
		/// <summary> Cmd_Say_f</summary>
		public static void  Say_f(edict_t ent, bool team, bool arg0)
		{
			
			int i, j;
			edict_t other;
			System.String text;
			gclient_t cl;
			
			if (Cmd.Argc() < 2 && !arg0)
				return ;
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			if (0 == ((int) (GameBase.dmflags.value_Renamed) & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS)))
				team = false;
			
			if (team)
				text = "(" + ent.client.pers.netname + "): ";
			else
				text = "" + ent.client.pers.netname + ": ";
			
			if (arg0)
			{
				text += Cmd.Argv(0);
				text += " ";
				text += Cmd.Args();
			}
			else
			{
				if (Cmd.Args().StartsWith("\""))
					text += Cmd.Args().Substring(1, (Cmd.Args().Length - 1) - (1));
				else
					text += Cmd.Args();
			}
			
			// don't let text be too long for malicious reasons
			if (text.Length > 150)
			//text[150] = 0;
				text = text.Substring(0, (150) - (0));
			
			text += "\n";
			
			if (GameBase.flood_msgs.value_Renamed != 0)
			{
				cl = ent.client;
				
				if (GameBase.level.time < cl.flood_locktill)
				{
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					SV_GAME.PF_cprintfhigh(ent, "You can't talk for " + (int) (cl.flood_locktill - GameBase.level.time) + " more seconds\n");
					return ;
				}
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				i = (int) (cl.flood_whenhead - GameBase.flood_msgs.value_Renamed + 1);
				if (i < 0)
					i = (10) + i;
				if (cl.flood_when[i] != 0 && GameBase.level.time - cl.flood_when[i] < GameBase.flood_persecond.value_Renamed)
				{
					cl.flood_locktill = GameBase.level.time + GameBase.flood_waitdelay.value_Renamed;
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					SV_GAME.PF_cprintf(ent, Defines.PRINT_CHAT, "Flood protection:  You can't talk for " + (int) GameBase.flood_waitdelay.value_Renamed + " seconds.\n");
					return ;
				}
				
				cl.flood_whenhead = (cl.flood_whenhead + 1) % 10;
				cl.flood_when[cl.flood_whenhead] = GameBase.level.time;
			}
			
			if (Globals.dedicated.value_Renamed != 0)
				SV_GAME.PF_cprintf(null, Defines.PRINT_CHAT, "" + text + "");
			
			for (j = 1; j <= GameBase.game.maxclients; j++)
			{
				other = GameBase.g_edicts[j];
				if (!other.inuse)
					continue;
				if (other.client == null)
					continue;
				if (team)
				{
					if (!GameUtil.OnSameTeam(ent, other))
						continue;
				}
				SV_GAME.PF_cprintf(other, Defines.PRINT_CHAT, "" + text + "");
			}
		}
		
		/// <summary> Returns the playerlist. TODO: The list is badly formatted at the moment.</summary>
		public static void  PlayerList_f(edict_t ent)
		{
			int i;
			System.String st;
			System.String text;
			edict_t e2;
			
			// connect time, ping, score, name
			text = "";
			
			for (i = 0; i < GameBase.maxclients.value_Renamed; i++)
			{
				e2 = GameBase.g_edicts[1 + i];
				if (!e2.inuse)
					continue;
				
				st = "" + (GameBase.level.framenum - e2.client.resp.enterframe) / 600 + ":" + ((GameBase.level.framenum - e2.client.resp.enterframe) % 600) / 10 + " " + e2.client.ping + " " + e2.client.resp.score + " " + e2.client.pers.netname + " " + (e2.client.resp.spectator?" (spectator)":"") + "\n";
				
				if (text.Length + st.Length > 1024 - 50)
				{
					text += "And more...\n";
					SV_GAME.PF_cprintfhigh(ent, "" + text + "");
					return ;
				}
				text += st;
			}
			SV_GAME.PF_cprintfhigh(ent, text);
		}
		
		/// <summary> Adds the current command line as a clc_stringcmd to the client message.
		/// things like godmode, noclip, etc, are commands directed to the server, so
		/// when they are typed in at the console, they will need to be forwarded.
		/// </summary>
		public static void  ForwardToServer()
		{
			System.String cmd;
			
			cmd = Cmd.Argv(0);
			if (Globals.cls.state <= Defines.ca_connected || cmd[0] == '-' || cmd[0] == '+')
			{
				Com.Printf("Unknown command \"" + cmd + "\"\n");
				return ;
			}
			
			MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_stringcmd);
			SZ.Print(Globals.cls.netchan.message, cmd);
			if (Cmd.Argc() > 1)
			{
				SZ.Print(Globals.cls.netchan.message, " ");
				SZ.Print(Globals.cls.netchan.message, Cmd.Args());
			}
		}
		
		/// <summary> Cmd_CompleteCommand.</summary>
		public static System.Collections.ArrayList CompleteCommand(System.String partial)
		{
			System.Collections.ArrayList cmds = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
			
			// check for match
			for (cmd_function_t cmd = cmd_functions; cmd != null; cmd = cmd.next)
				if (cmd.name.StartsWith(partial))
					cmds.Add(cmd.name);
			for (cmdalias_t a = Globals.cmd_alias; a != null; a = a.next)
				if (a.name.StartsWith(partial))
					cmds.Add(a.name);
			
			return cmds;
		}
		
		/// <summary> Processes the commands the player enters in the quake console.</summary>
		public static void  ClientCommand(edict_t ent)
		{
			System.String cmd;
			
			if (ent.client == null)
				return ; // not fully in game yet
			
			cmd = GameBase.gi.argv(0).ToLower();
			
			if (cmd.Equals("players"))
			{
				Players_f(ent);
				return ;
			}
			if (cmd.Equals("say"))
			{
				Say_f(ent, false, false);
				return ;
			}
			if (cmd.Equals("say_team"))
			{
				Say_f(ent, true, false);
				return ;
			}
			if (cmd.Equals("score"))
			{
				Score_f(ent);
				return ;
			}
			if (cmd.Equals("help"))
			{
				Help_f(ent);
				return ;
			}
			
			if (GameBase.level.intermissiontime != 0)
				return ;
			
			if (cmd.Equals("use"))
				Use_f(ent);
			else if (cmd.Equals("drop"))
				Drop_f(ent);
			else if (cmd.Equals("give"))
				Give_f(ent);
			else if (cmd.Equals("god"))
				God_f(ent);
			else if (cmd.Equals("notarget"))
				Notarget_f(ent);
			else if (cmd.Equals("noclip"))
				Noclip_f(ent);
			else if (cmd.Equals("inven"))
				Inven_f(ent);
			else if (cmd.Equals("invnext"))
				GameItems.SelectNextItem(ent, - 1);
			else if (cmd.Equals("invprev"))
				GameItems.SelectPrevItem(ent, - 1);
			else if (cmd.Equals("invnextw"))
				GameItems.SelectNextItem(ent, Defines.IT_WEAPON);
			else if (cmd.Equals("invprevw"))
				GameItems.SelectPrevItem(ent, Defines.IT_WEAPON);
			else if (cmd.Equals("invnextp"))
				GameItems.SelectNextItem(ent, Defines.IT_POWERUP);
			else if (cmd.Equals("invprevp"))
				GameItems.SelectPrevItem(ent, Defines.IT_POWERUP);
			else if (cmd.Equals("invuse"))
				InvUse_f(ent);
			else if (cmd.Equals("invdrop"))
				InvDrop_f(ent);
			else if (cmd.Equals("weapprev"))
				WeapPrev_f(ent);
			else if (cmd.Equals("weapnext"))
				WeapNext_f(ent);
			else if (cmd.Equals("weaplast"))
				WeapLast_f(ent);
			else if (cmd.Equals("kill"))
				Kill_f(ent);
			else if (cmd.Equals("putaway"))
				PutAway_f(ent);
			else if (cmd.Equals("wave"))
				Wave_f(ent);
			else if (cmd.Equals("playerlist"))
				PlayerList_f(ent);
			else if (cmd.Equals("showposition"))
				ShowPosition_f(ent);
			// anything that doesn't match a command will be a chat
			else
				Say_f(ent, false, true);
		}
		
		public static void  ValidateSelectedItem(edict_t ent)
		{
			gclient_t cl = ent.client;
			
			if (cl.pers.inventory[cl.pers.selected_item] != 0)
				return ; // valid
			
			GameItems.SelectNextItem(ent, - 1);
		}
		static Cmd()
		{
			List_f = new AnonymousClassxcommand_t();
			Exec_f = new AnonymousClassxcommand_t1();
			Echo_f = new AnonymousClassxcommand_t2();
			Alias_f = new AnonymousClassxcommand_t3();
			Wait_f = new AnonymousClassxcommand_t4();
			cmd_argv = new System.String[Defines.MAX_STRING_TOKENS];
			expanded = new char[Defines.MAX_STRING_CHARS];
			temporary = new char[Defines.MAX_STRING_CHARS];
			PlayerSort = new AnonymousClassComparator();
		}
	}
}